﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using Cloak.Linq;

namespace Ruleweaver
{
	public static class Checkable
	{
		#region Passes / Fails

		public static Check<T> Passes<T>(this ICheckable<T> check, Func<T, bool> nextCheck)
		{
			Contract.Requires(check != null);
			Contract.Requires(nextCheck != null);

			return new PassesCheck<T>(check, nextCheck);
		}

		public static Check<T> Passes<T>(this ICheckable<T> check, bool nextCheckResult)
		{
			Contract.Requires(check != null);

			return new FixedResultCheck<T>(check, nextCheckResult);
		}

		public static Check<T> Fails<T>(this ICheckable<T> check, Func<T, bool> nextCheck)
		{
			Contract.Requires(check != null);
			Contract.Requires(nextCheck != null);

			return check.Passes(t => !nextCheck(t));
		}

		public static Check<T> Fails<T>(this ICheckable<T> check, bool nextCheckResult)
		{
			Contract.Requires(check != null);

			return new FixedResultCheck<T>(check, !nextCheckResult);
		}

		private sealed class PassesCheck<T> : Check<T>
		{
			private readonly ICheckable<T> _baseCheck;
			private readonly Func<T, bool> _nextCheck;

			internal PassesCheck(ICheckable<T> baseCheck, Func<T, bool> nextCheck) : base(baseCheck.Target)
			{
				_baseCheck = baseCheck;
				_nextCheck = nextCheck;
			}

			public override bool Apply()
			{
				return _baseCheck.Apply() && _nextCheck(Target);
			}
		}

		private sealed class FixedResultCheck<T> : Check<T>
		{
			private readonly ICheckable<T> _baseCheck;
			private readonly bool _nextCheckResult;

			internal FixedResultCheck(ICheckable<T> baseCheck, bool nextCheckResult) : base(baseCheck.Target)
			{
				_baseCheck = baseCheck;
				_nextCheckResult = nextCheckResult;
			}

			public override bool Apply()
			{
				return _baseCheck.Apply() && _nextCheckResult;
			}
		}
		#endregion

		#region Boolean

		public static Check<bool> IsTrue(this ICheckable<bool> check)
		{
			Contract.Requires(check != null);

			return check.Passes(t => t);
		}

		public static Check<bool> IsFalse(this ICheckable<bool> check)
		{
			Contract.Requires(check != null);

			return check.Passes(t => !t);
		}
		#endregion

		#region Char

		public static Check<char> IsLetterOrDigit(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsLetterOrDigit(c));
		}

		public static Check<char> IsControl(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsControl(c));
		}

		public static Check<char> IsDigit(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsDigit(c));
		}

		public static Check<char> IsHighSurrogate(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsHighSurrogate(c));
		}

		public static Check<char> IsLetter(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsLetter(c));
		}

		public static Check<char> IsLowerCase(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsLower(c));
		}

		public static Check<char> IsLowSurrogate(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsLowSurrogate(c));
		}

		public static Check<char> IsNumber(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsNumber(c));
		}

		public static Check<char> IsPunctuation(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsPunctuation(c));
		}

		public static Check<char> IsSeparator(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsSeparator(c));
		}

		public static Check<char> IsSurrogate(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsSurrogate(c));
		}

		public static Check<char> IsSymbol(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsSymbol(c));
		}

		public static Check<char> IsUpperCase(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsUpper(c));
		}

		public static Check<char> IsWhiteSpace(this ICheckable<char> check)
		{
			Contract.Requires(check != null);

			return check.Passes(c => Char.IsWhiteSpace(c));
		}
		#endregion

		#region DateTime

		public static Check<DateTime> IsWeekend(this ICheckable<DateTime> check)
		{
			Contract.Requires(check != null);

			return check.Passes(d => d.DayOfWeek == DayOfWeek.Saturday || d.DayOfWeek == DayOfWeek.Sunday);
		}

		public static Check<DateTime> IsWeekday(this ICheckable<DateTime> check)
		{
			Contract.Requires(check != null);

			return check.Passes(d => d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday);
		}

		public static Check<DateTime> IsLeapYear(this ICheckable<DateTime> check)
		{
			Contract.Requires(check != null);

			return check.Passes(d => DateTime.IsLeapYear(d.Year));
		}
		#endregion

		#region Decimal

		public static Check<decimal> IsEven(this ICheckable<decimal> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 0);
		}

		public static Check<decimal> IsOdd(this ICheckable<decimal> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 1);
		}

		public static Check<decimal> IsPositive(this ICheckable<decimal> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n > 0);
		}

		public static Check<decimal> IsNegative(this ICheckable<decimal> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n < 0);
		}

		public static Check<decimal> IsNotPositive(this ICheckable<decimal> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n <= 0);
		}

		public static Check<decimal> IsNotNegative(this ICheckable<decimal> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0);
		}

		public static Check<decimal> IsAdjustedPercentage(this ICheckable<decimal> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0 && n <= 100);
		}

		public static Check<decimal> IsLiteralPercentage(this ICheckable<decimal> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0 && n <= 1);
		}
		#endregion

		#region Double

		public static Check<double> IsEven(this ICheckable<double> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 0);
		}

		public static Check<double> IsOdd(this ICheckable<double> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 1);
		}

		public static Check<double> IsPositive(this ICheckable<double> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n > 0);
		}

		public static Check<double> IsNegative(this ICheckable<double> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n < 0);
		}

		public static Check<double> IsNotPositive(this ICheckable<double> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n <= 0);
		}

		public static Check<double> IsNotNegative(this ICheckable<double> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0);
		}

		public static Check<double> IsAdjustedPercentage(this ICheckable<double> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0 && n <= 100);
		}

		public static Check<double> IsLiteralPercentage(this ICheckable<double> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0 && n <= 1);
		}
		#endregion

		#region Enumerable

		public static Check<IEnumerable<T>> Contains<T>(this ICheckable<IEnumerable<T>> check, T value)
		{
			Contract.Requires(check != null);

			return check.Passes(source => source != null && source.Contains(value));
		}

		public static Check<IEnumerable<T>> Contains<T>(
			this ICheckable<IEnumerable<T>> check,
			T value,
			IEqualityComparer<T> comparer)
		{
			Contract.Requires(check != null);
			Contract.Requires(comparer != null);

			return check.Passes(source => source != null && source.Contains(value, comparer));
		}

		public static Check<IEnumerable<T>> HasNone<T>(this ICheckable<IEnumerable<T>> check)
		{
			Contract.Requires(check != null);

			return check.Passes(source => source != null && !source.Any());
		}

		public static Check<IEnumerable<T>> HasNone<T>(this ICheckable<IEnumerable<T>> check, Func<T, bool> itemCheck)
		{
			Contract.Requires(check != null);
			Contract.Requires(itemCheck != null);

			return check.Passes(source => source != null && !source.Any(itemCheck));
		}

		public static Check<IEnumerable<T>> HasAny<T>(this ICheckable<IEnumerable<T>> check)
		{
			Contract.Requires(check != null);

			return check.Passes(source => source != null && source.Any());
		}

		public static Check<IEnumerable<T>> HasAny<T>(this ICheckable<IEnumerable<T>> check, Func<T, bool> itemCheck)
		{
			Contract.Requires(check != null);
			Contract.Requires(itemCheck != null);

			return check.Passes(source => source != null && source.Any(itemCheck));
		}

		public static Check<IEnumerable<T>> HasAll<T>(this ICheckable<IEnumerable<T>> check, Func<T, bool> itemCheck)
		{
			Contract.Requires(check != null);
			Contract.Requires(itemCheck != null);

			return check.Passes(source => source != null && source.All(itemCheck));
		}
		#endregion

		#region In

		public static Check<T> IsIn<T>(this ICheckable<T> check, IEnumerable<T> values)
		{
			Contract.Requires(check != null);
			Contract.Requires(values != null);

			return check.Passes(t => values.Contains(t));
		}

		public static Check<T> IsIn<T>(this ICheckable<T> check, params T[] values)
		{
			Contract.Requires(check != null);
			Contract.Requires(values != null);

			return check.IsIn(values as IEnumerable<T>);
		}

		public static Check<T> IsIn<T>(this ICheckable<T> check, IQueryable<T> values)
		{
			Contract.Requires(check != null);
			Contract.Requires(values != null);

			return check.Passes(t => values.Contains(t));
		}

		public static Check<T> IsIn<T>(
			this ICheckable<T> check,
			IEqualityComparer<T> comparer,
			IEnumerable<T> values)
		{
			Contract.Requires(check != null);
			Contract.Requires(comparer != null);
			Contract.Requires(values != null);

			return check.Passes(t => values.Contains(t, comparer));
		}

		public static Check<T> IsIn<T>(
			this ICheckable<T> check,
			IEqualityComparer<T> comparer,
			params T[] values)
		{
			Contract.Requires(check != null);
			Contract.Requires(comparer != null);
			Contract.Requires(values != null);

			return check.IsIn(comparer, values as IEnumerable<T>);
		}

		public static Check<T> IsIn<T>(
			this ICheckable<T> check,
			IEqualityComparer<T> comparer,
			IQueryable<T> values)
		{
			Contract.Requires(check != null);
			Contract.Requires(comparer != null);
			Contract.Requires(values != null);

			return check.Passes(t => values.Contains(t, comparer));
		}
		#endregion

		#region Int32

		public static Check<int> IsEven(this ICheckable<int> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 0);
		}

		public static Check<int> IsOdd(this ICheckable<int> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 1);
		}

		public static Check<int> IsPositive(this ICheckable<int> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n > 0);
		}

		public static Check<int> IsNegative(this ICheckable<int> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n < 0);
		}

		public static Check<int> IsNotPositive(this ICheckable<int> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n <= 0);
		}

		public static Check<int> IsNotNegative(this ICheckable<int> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0);
		}

		public static Check<int> IsPercentage(this ICheckable<int> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0 && n <= 100);
		}
		#endregion

		#region Int64

		public static Check<long> IsEven(this ICheckable<long> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 0);
		}

		public static Check<long> IsOdd(this ICheckable<long> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 1);
		}

		public static Check<long> IsPositive(this ICheckable<long> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n > 0);
		}

		public static Check<long> IsNegative(this ICheckable<long> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n < 0);
		}

		public static Check<long> IsNotPositive(this ICheckable<long> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n <= 0);
		}

		public static Check<long> IsNotNegative(this ICheckable<long> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0);
		}

		public static Check<long> IsPercentage(this ICheckable<long> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0 && n <= 100);
		}
		#endregion

		#region Nullity

		public static Check<T> IsNull<T>(this ICheckable<T> check)
		{
			Contract.Requires(check != null);

			return check.Passes(t => t == null);
		}

		public static Check<T> IsNotNull<T>(this ICheckable<T> check)
		{
			Contract.Requires(check != null);

			return check.Passes(t => t != null);
		}
		#endregion

		#region Queryable

		public static Check<IQueryable<T>> Contains<T>(this ICheckable<IQueryable<T>> check, T value)
		{
			Contract.Requires(check != null);

			return check.Passes(source => source != null && source.Contains(value));
		}

		public static Check<IQueryable<T>> Contains<T>(
			this ICheckable<IQueryable<T>> check,
			T value,
			IEqualityComparer<T> comparer)
		{
			Contract.Requires(check != null);
			Contract.Requires(comparer != null);

			return check.Passes(source => source != null && source.Contains(value, comparer));
		}

		public static Check<IQueryable<T>> HasNone<T>(this ICheckable<IQueryable<T>> check)
		{
			Contract.Requires(check != null);

			return check.Passes(source => source != null && !source.Any());
		}

		public static Check<IQueryable<T>> HasNone<T>(this ICheckable<IQueryable<T>> check, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(check != null);
			Contract.Requires(itemCheck != null);

			return check.Passes(source => source != null && !source.Any(itemCheck));
		}

		public static Check<IQueryable<T>> HasAny<T>(this ICheckable<IQueryable<T>> check)
		{
			Contract.Requires(check != null);

			return check.Passes(source => source != null && source.Any());
		}

		public static Check<IQueryable<T>> HasAny<T>(this ICheckable<IQueryable<T>> check, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(check != null);
			Contract.Requires(itemCheck != null);

			return check.Passes(source => source != null && source.Any(itemCheck));
		}

		public static Check<IQueryable<T>> HasAll<T>(this ICheckable<IQueryable<T>> check, Expression<Func<T, bool>> itemCheck)
		{
			Contract.Requires(check != null);
			Contract.Requires(itemCheck != null);

			return check.Passes(source => source != null && source.All(itemCheck));
		}
		#endregion

		#region Relativity

		public static Check<T> IsLessThan<T>(this ICheckable<T> check, T value) where T : IComparable<T>
		{
			Contract.Requires(check != null);

			return check.Passes(t => t != null && t.CompareTo(value) < 0);
		}

		public static Check<T> IsLessThanOrEqualTo<T>(this ICheckable<T> check, T value) where T : IComparable<T>
		{
			Contract.Requires(check != null);

			return check.Passes(t => t != null && t.CompareTo(value) <= 0);
		}

		public static Check<T> IsEqualTo<T>(this ICheckable<T> check, T value, IEqualityComparer<T> equalityComparer)
		{
			Contract.Requires(check != null);
			Contract.Requires(equalityComparer != null);

			return check.Passes(t => equalityComparer.Equals(t, value));
		}

		public static Check<T> IsEqualTo<T>(this ICheckable<T> check, T value)
		{
			Contract.Requires(check != null);

			return check.IsEqualTo(value, EqualityComparer<T>.Default);
		}

		public static Check<T> IsGreaterThanOrEqualTo<T>(this ICheckable<T> check, T value) where T : IComparable<T>
		{
			Contract.Requires(check != null);

			return check.Passes(t => t != null && t.CompareTo(value) >= 0);
		}

		public static Check<T> IsGreaterThan<T>(this ICheckable<T> check, T value) where T : IComparable<T>
		{
			Contract.Requires(check != null);

			return check.Passes(t => t != null && t.CompareTo(value) > 0);
		}

		public static Check<T> IsBetween<T>(this ICheckable<T> check, T minimum, T maximum, BoundaryType boundaryType)
			where T : IComparable<T>
		{
			Contract.Requires(check != null);
			Contract.Requires(minimum == null || minimum.CompareTo(maximum) <= 0);

			if(check.Target != null)
			{
				switch(boundaryType)
				{
					case BoundaryType.Inclusive:
						return check.Passes(t => t.CompareTo(minimum) >= 0 && t.CompareTo(maximum) <= 0);
					case BoundaryType.ExcludeMinimum:
						return check.Passes(t => t.CompareTo(minimum) > 0 && t.CompareTo(maximum) <= 0);
					case BoundaryType.ExcludeMaximum:
						return check.Passes(t => t.CompareTo(minimum) >= 0 && t.CompareTo(maximum) < 0);
					case BoundaryType.Exclusive:
						return check.Passes(t => t.CompareTo(minimum) > 0 && t.CompareTo(maximum) < 0);
					default:
						throw new ArgumentOutOfRangeException("boundaryType");
				}
			}
			else
			{
				bool isInNullRange;

				switch(boundaryType)
				{
					case BoundaryType.Inclusive:
						isInNullRange = minimum == null && maximum == null;
						break;
					case BoundaryType.ExcludeMinimum:
						isInNullRange = maximum == null;
						break;
					case BoundaryType.ExcludeMaximum:
						isInNullRange = minimum == null;
						break;
					case BoundaryType.Exclusive:
						isInNullRange = false;
						break;
					default:
						throw new ArgumentOutOfRangeException("boundaryType");
				}

				return check.Passes(isInNullRange);
			}
		}

		public static Check<T> IsBetween<T>(this ICheckable<T> check, T minimum, T maximum)
			where T : IComparable<T>
		{
			Contract.Requires(check != null);
			Contract.Requires(minimum == null || minimum.CompareTo(maximum) <= 0);

			return check.IsBetween(minimum, maximum, default(BoundaryType));
		}
		#endregion

		#region Single

		public static Check<float> IsEven(this ICheckable<float> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 0);
		}

		public static Check<float> IsOdd(this ICheckable<float> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n % 2 == 1);
		}

		public static Check<float> IsPositive(this ICheckable<float> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n > 0);
		}

		public static Check<float> IsNegative(this ICheckable<float> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n < 0);
		}

		public static Check<float> IsNotPositive(this ICheckable<float> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n <= 0);
		}

		public static Check<float> IsNotNegative(this ICheckable<float> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0);
		}

		public static Check<float> IsAdjustedPercentage(this ICheckable<float> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0 && n <= 100);
		}

		public static Check<float> IsLiteralPercentage(this ICheckable<float> check)
		{
			Contract.Requires(check != null);

			return check.Passes(n => n >= 0 && n <= 1);
		}
		#endregion

		#region String Content

		public static Check<string> IsEmpty(this ICheckable<string> check)
		{
			Contract.Requires(check != null);

			return check.Passes(s => s == "");
		}

		public static Check<string> IsNotEmpty(this ICheckable<string> check)
		{
			Contract.Requires(check != null);

			return check.Passes(s => s != "");
		}

		public static Check<string> IsNotNullOrEmpty(this ICheckable<string> check)
		{
			Contract.Requires(check != null);

			return check.Passes(s => !String.IsNullOrEmpty(s));
		}

		public static Check<string> StartsWith(this ICheckable<string> check, string value)
		{
			Contract.Requires(check != null);
			Contract.Requires(!String.IsNullOrEmpty(value));

			return check.Passes(s => !String.IsNullOrEmpty(s) && s.StartsWith(value));
		}

		public static Check<string> StartsWith(this ICheckable<string> check, string value, StringComparison comparisonType)
		{
			Contract.Requires(check != null);
			Contract.Requires(!String.IsNullOrEmpty(value));

			return check.Passes(s => !String.IsNullOrEmpty(s) && s.StartsWith(value, comparisonType));
		}

		public static Check<string> StartsWith(this ICheckable<string> check, string value, bool ignoreCase, CultureInfo culture)
		{
			Contract.Requires(check != null);
			Contract.Requires(!String.IsNullOrEmpty(value));
			Contract.Requires(culture != null);

			return check.Passes(s => !String.IsNullOrEmpty(s) && s.StartsWith(value, ignoreCase, culture));
		}

		public static Check<string> EndsWith(this ICheckable<string> check, string value)
		{
			Contract.Requires(check != null);
			Contract.Requires(!String.IsNullOrEmpty(value));

			return check.Passes(s => !String.IsNullOrEmpty(s) && s.EndsWith(value));
		}

		public static Check<string> EndsWith(this ICheckable<string> check, string value, StringComparison comparisonType)
		{
			Contract.Requires(check != null);
			Contract.Requires(!String.IsNullOrEmpty(value));

			return check.Passes(s => !String.IsNullOrEmpty(s) && s.EndsWith(value, comparisonType));
		}

		public static Check<string> EndsWith(this ICheckable<string> check, string value, bool ignoreCase, CultureInfo culture)
		{
			Contract.Requires(check != null);
			Contract.Requires(!String.IsNullOrEmpty(value));
			Contract.Requires(culture != null);

			return check.Passes(s => !String.IsNullOrEmpty(s) && s.EndsWith(value, ignoreCase, culture));
		}

		public static Check<string> Contains(this ICheckable<string> check, string value)
		{
			Contract.Requires(check != null);
			Contract.Requires(value != null);

			return check.Passes(s => s != null && s.Contains(value));
		}
		#endregion

		#region String Patterns

		public static Check<string> Matches(this ICheckable<string> check, Regex regex)
		{
			Contract.Requires(regex != null);

			return check.Passes(s => s != null && regex.IsMatch(s));
		}

		public static Check<string> Matches(this ICheckable<string> check, string pattern)
		{
			Contract.Requires(pattern != null);

			return check.Passes(s => s != null && Regex.IsMatch(s, pattern));
		}

		public static Check<string> Matches(this ICheckable<string> check, string pattern, RegexOptions options)
		{
			Contract.Requires(pattern != null);

			return check.Passes(s => s != null && Regex.IsMatch(s, pattern, options));
		}

		public static Check<string> IsCreditCardNumber(this ICheckable<string> check)
		{
			Contract.Requires(check != null);

			return check.Passes(target =>
			{
				// Adapted from http://mikehadlow.blogspot.com/2008/05/credit-card-validation-with-linq.html

				var valid = target != null && target.Length >= 13 && target.Length <= 18;

				if(valid)
				{
					var digits =
						(from ch in target.Reverse()
							where Char.IsDigit(ch)
							select Int32.Parse(ch.ToString()))
							.ToList();

					if(digits.Count != target.Length)
					{
						// Invalid character
						valid = false;
					}
					else
					{
						var doubleEvenSum = digits
							.AtEvenPositions()
							.SelectMany(d => new[] { (d * 2) % 10, (d * 2) / 10 })
							.Sum();

						valid = (digits.AtOddPositions().Sum() + doubleEvenSum) % 10 == 0;
					}
				}

				return valid;
			});
		}

		public static Check<string> IsCreditCardCvv(this ICheckable<string> check)
		{
			Contract.Requires(check != null);

			return check.Matches(Resources.CreditCardCvvPattern);
		}

		public static Check<string> IsPhoneNumber(this ICheckable<string> check)
		{
			Contract.Requires(check != null);

			return check.Matches(Resources.PhoneNumberPattern);
		}

		public static Check<string> IsSocialSecurityNumber(this ICheckable<string> check)
		{
			Contract.Requires(check != null);

			return check.Matches(Resources.SocialSecurityNumberPattern);
		}

		public static Check<string> IsZipCode(this ICheckable<string> check)
		{
			Contract.Requires(check != null);

			return check.Matches(Resources.ZipCodePattern);
		}
		#endregion

		public static Check<T> IsAssignableTo<T>(this ICheckable<T> check, Type type)
		{
			Contract.Requires(check != null);
			Contract.Requires(type != null);

			return check.Passes(t => type == typeof(T) || IsAssignableTo(t, type));
		}

		private static bool IsAssignableTo<T>(T target, Type type)
		{
			return (typeof(T).IsValueType || target != null) && type.IsAssignableFrom(target.GetType());
		}
	}
}